home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre4.z / postgre4 / src / storage / lmgr / proc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-27  |  15.4 KB  |  635 lines

  1. /*
  2.  * proc.c -- routines to manage per-process shared memory data
  3.  *    structure
  4.  *
  5.  *  Each postgres backend gets one of these.  We'll use it to
  6.  *  clean up after the process should the process suddenly die.
  7.  *
  8.  *
  9.  * Interface (a):
  10.  *    ProcSleep(), ProcWakeup(), ProcWakeupNext(),
  11.  *     ProcQueueAlloc() -- create a shm queue for sleeping processes
  12.  *     ProcQueueInit() -- create a queue without allocing memory
  13.  *
  14.  * Locking and waiting for buffers can cause the backend to be
  15.  * put to sleep.  Whoever releases the lock, etc. wakes the
  16.  * process up again (and gives it an error code so it knows
  17.  * whether it was awoken on an error condition).
  18.  *
  19.  * Interface (b):
  20.  *
  21.  * ProcReleaseLocks -- frees the locks associated with this process,
  22.  * ProcKill -- destroys the shared memory state (and locks)
  23.  *    associated with the process.
  24.  *
  25.  * 5/15/91 -- removed the buffer pool based lock chain in favor
  26.  *    of a shared memory lock chain.  The write-protection is
  27.  *    more expensive if the lock chain is in the buffer pool.
  28.  *    The only reason I kept the lock chain in the buffer pool
  29.  *    in the first place was to allow the lock table to grow larger
  30.  *    than available shared memory and that isn't going to work
  31.  *    without a lot of unimplemented support anyway.
  32.  *
  33.  * $Header: /private/postgres/src/storage/lmgr/RCS/proc.c,v 1.14 1992/06/17 04:58:19 mer Exp $
  34.  */
  35. #include <sys/time.h>
  36. #include <signal.h>
  37.  
  38. #include "utils/hsearch.h"
  39. #include "utils/log.h"
  40.  
  41. #include "storage/buf.h"    
  42. #include "storage/lock.h"
  43. #include "storage/shmem.h"
  44. #include "storage/spin.h"
  45. #include "storage/proc.h"
  46. #include "storage/procq.h"
  47.  
  48. extern int MyPid;    /* for parallel backends w/same xid */
  49.  
  50. /* --------------------
  51.  * Spin lock for manipulating the shared process data structure:
  52.  * ProcGlobal.... Adding an extra spin lock seemed like the smallest
  53.  * hack to get around reading and updating this structure in shared
  54.  * memory. -mer 17 July 1991
  55.  * --------------------
  56.  */
  57. SPINLOCK ProcStructLock;
  58.  
  59. /*
  60.  * For cleanup routines.  Don't cleanup if the initialization
  61.  * has not happened.
  62.  */
  63. Boolean    ProcInitialized = FALSE;
  64.  
  65. PROC_HDR *ProcGlobal = NULL;
  66.  
  67. PROC         *MyProc = NULL;
  68.  
  69. /* ------------------------
  70.  * InitProc -- create a per-process data structure for this process
  71.  * used by the lock manager on semaphore queues.
  72.  * ------------------------
  73.  */
  74. void
  75. InitProcess(key)
  76. IPCKey key;
  77. {
  78.   bool found = false;
  79.   int pid;
  80.   int semstat;
  81.   unsigned int location, myOffset;
  82.  
  83.   /* ------------------
  84.    * Routine called if deadlock timer goes off. See ProcSleep()
  85.    * ------------------
  86.    */
  87.   signal(SIGALRM, HandleDeadLock);
  88.  
  89.   SpinAcquire(ProcStructLock);
  90.  
  91.   /* attach to the free list */
  92.   ProcGlobal = (PROC_HDR *)
  93.     ShmemInitStruct("Proc Header",(unsigned)sizeof(PROC_HDR),&found);
  94.  
  95.   /* --------------------
  96.    * We're the first - initialize.
  97.    * --------------------
  98.    */
  99.   if (! found)
  100.   {
  101.     ProcGlobal->numProcs = 0;
  102.     ProcGlobal->freeProcs = INVALID_OFFSET;
  103.     ProcGlobal->currKey = IPCGetProcessSemaphoreInitKey(key);
  104.   }
  105.  
  106.   if (MyProc != NULL)
  107.   {
  108.     SpinRelease(ProcStructLock);
  109.     elog(WARN,"ProcInit: you already exist");
  110.     return;
  111.   }
  112.  
  113.   /* try to get a proc from the free list first */
  114.  
  115.   myOffset = ProcGlobal->freeProcs;
  116.  
  117.   if (myOffset != INVALID_OFFSET)
  118.   {
  119.     MyProc = (PROC *) MAKE_PTR(myOffset);
  120.     ProcGlobal->freeProcs = MyProc->links.next;
  121.   }
  122.   else
  123.   {
  124.     /* have to allocate one.  We can't use the normal binding
  125.      * table mechanism because the proc structure is stored
  126.      * by PID instead of by a global name (need to look it
  127.      * up by PID when we cleanup dead processes).
  128.      */
  129.  
  130.     MyProc = (PROC *) ShmemAlloc((unsigned)sizeof(PROC));
  131.     if (! MyProc)
  132.     {
  133.       SpinRelease(ProcStructLock);
  134.       elog (FATAL,"cannot create new proc: out of memory");
  135.     }
  136.  
  137.     /* this cannot be initialized until after the buffer pool */
  138.     SHMQueueInit(&(MyProc->lockQueue));
  139.     MyProc->procId = ProcGlobal->numProcs;
  140.     ProcGlobal->numProcs++;
  141.   }
  142.  
  143.   /*
  144.    * zero out the spin lock counts and set the sLocks field for ProcStructLock 
  145.    * to * 1 as we have acquired this spinlock above but didn't record it since
  146.    * we didn't have MyProc until now.
  147.    */
  148.   bzero(MyProc->sLocks, sizeof(MyProc->sLocks));
  149.   MyProc->sLocks[ProcStructLock] = 1;
  150.  
  151.   MyProc->sem.semId = IpcSemaphoreCreate(ProcGlobal->currKey,
  152.                                          1,
  153.                                          IPCProtection,
  154.                                          IpcSemaphoreDefaultStartValue,
  155.                                          &semstat);
  156.   IpcSemaphoreLock(MyProc->sem.semId, 0, IpcExclusiveLock);
  157.  
  158.   /* -------------
  159.    * Bump key val for next process
  160.    * -------------
  161.    */
  162.   MyProc->sem.semKey = ProcGlobal->currKey;
  163.   ProcGlobal->currKey++;
  164.  
  165.   /* ----------------------
  166.    * Release the lock.
  167.    * ----------------------
  168.    */
  169.   SpinRelease(ProcStructLock);
  170.  
  171.   MyProc->sem.semNum = 0;
  172.   MyProc->pid = MyPid;
  173.  
  174.   /* ----------------
  175.    * Start keeping spin lock stats from here on.  Any botch before
  176.    * this initialization is forever botched
  177.    * ----------------
  178.    */
  179.   bzero(MyProc->sLocks, MAX_SPINS*sizeof(*MyProc->sLocks));
  180.  
  181.   /* -------------------------
  182.    * Install ourselves in the binding table.  The name to
  183.    * use is determined by the OS-assigned process id.  That
  184.    * allows the cleanup process to find us after any untimely
  185.    * exit.
  186.    * -------------------------
  187.    */
  188.   pid = getpid();
  189.   location = MAKE_OFFSET(MyProc);
  190.   if ((! ShmemPIDLookup(pid,&location)) || (location != MAKE_OFFSET(MyProc)))
  191.   {
  192.     elog(FATAL,"InitProc: ShmemPID table broken");
  193.   }
  194.  
  195.   MyProc->errType = NO_ERROR;
  196.   SHMQueueElemInit(&(MyProc->links));
  197.  
  198.   on_exitpg(ProcKill, pid);
  199.  
  200.   ProcInitialized = TRUE;
  201. }
  202.  
  203. /*
  204.  * ProcReleaseLocks() -- release all locks associated with this process
  205.  *
  206.  */
  207. void
  208. ProcReleaseLocks()
  209. {
  210.   if (!MyProc)
  211.     return;
  212.   LockReleaseAll(1,&MyProc->lockQueue);
  213. }
  214.  
  215. bool
  216. ProcSemaphoreKill(pid)
  217. int pid;
  218. {
  219.   SHMEM_OFFSET  location,ShmemPIDDestroy();
  220.   PROC *proc;
  221.  
  222.   location = INVALID_OFFSET;
  223.  
  224.   ShmemPIDLookup(pid,&location);
  225.   if (location == INVALID_OFFSET)
  226.     return(FALSE);
  227.  
  228.   proc = (PROC *) MAKE_PTR(location);
  229.   IpcSemaphoreKill(proc->sem.semKey);
  230.   return(TRUE);
  231. }
  232.  
  233. /*
  234.  * ProcKill() -- Destroy the per-proc data structure for
  235.  *    this process. Release any of its held spin locks.
  236.  */
  237. bool
  238. ProcKill(exitStatus, pid)
  239. int exitStatus;
  240. int pid;
  241. {
  242.   PROC         *proc;
  243.   SHMEM_OFFSET    location,ShmemPIDDestroy();
  244.  
  245.   /* -------------------- 
  246.    * If this is a FATAL exit the postmaster will have to kill all the existing
  247.    * backends and reinitialize shared memory.  So all we don't need to do
  248.    * anything here.
  249.    * --------------------
  250.    */
  251.   if (exitStatus != 0)
  252.     return(TRUE);
  253.  
  254.   if (! pid)
  255.   {
  256.     pid = getpid();
  257.   }
  258.  
  259.   location = ShmemPIDDestroy(pid);
  260.   if (location == INVALID_OFFSET)
  261.     return(FALSE);
  262.  
  263.   proc = (PROC *) MAKE_PTR(location);
  264.  
  265.   if (proc != MyProc)
  266.     Assert( pid != getpid() );
  267.   else
  268.     MyProc = NULL;
  269.     
  270.   /* ---------------
  271.    * Assume one lock table.
  272.    * ---------------
  273.    */
  274.   ProcReleaseSpins(proc);
  275.   LockReleaseAll(1,&proc->lockQueue);
  276.  
  277.   /* ----------------
  278.    * get off the wait queue
  279.    * ----------------
  280.    */
  281.   if (proc->links.next != INVALID_OFFSET)
  282.     SHMQueueDelete(&(proc->links));
  283.   SHMQueueElemInit(&(proc->links));
  284.  
  285.   SpinAcquire(ProcStructLock);
  286.  
  287.   /* ----------------------
  288.    * If ProcGlobal hasn't been initialized (i.e. in the postmaster)
  289.    * attach to it.
  290.    * ----------------------
  291.    */
  292.   if (!ProcGlobal)
  293.   {
  294.     bool found = false;
  295.  
  296.     ProcGlobal = (PROC_HDR *)
  297.       ShmemInitStruct("Proc Header",(unsigned)sizeof(PROC_HDR),&found);
  298.     if (!found)
  299.       elog(NOTICE, "ProcKill: Couldn't find ProcGlobal in the binding table");
  300.     ProcGlobal = (PROC_HDR *)NULL;
  301.     return(FALSE);
  302.   }
  303.   proc->links.next =  ProcGlobal->freeProcs;
  304.   ProcGlobal->freeProcs = MAKE_OFFSET(proc);
  305.  
  306.   SpinRelease(ProcStructLock);
  307.   return(TRUE);
  308. }
  309.  
  310. /*
  311.  * ProcQueue package: routines for putting processes to sleep
  312.  *     and  waking them up
  313.  */
  314.  
  315. /*
  316.  * ProcQueueAlloc -- alloc/attach to a shared memory process queue
  317.  *
  318.  * Returns: a pointer to the queue or NULL
  319.  * Side Effects: Initializes the queue if we allocated one
  320.  */
  321. PROC_QUEUE *
  322. ProcQueueAlloc(name)
  323. char *name;
  324. {
  325.   Boolean    found;
  326.   PROC_QUEUE *queue = (PROC_QUEUE *)
  327.     ShmemInitStruct(name,(unsigned)sizeof(PROC_QUEUE),&found);
  328.  
  329.   if (! queue)
  330.   {
  331.     return(NULL);
  332.   }
  333.   if (! found)
  334.   {
  335.     ProcQueueInit(queue);
  336.   }
  337.   return(queue);
  338. }
  339. /*
  340.  * ProcQueueInit -- initialize a shared memory process queue
  341.  */
  342. void
  343. ProcQueueInit(queue)
  344. PROC_QUEUE *queue;
  345. {
  346.   SHMQueueInit(&(queue->links));
  347.   queue->size = 0;
  348. }
  349.  
  350.  
  351.  
  352. /*
  353.  * ProcSleep -- put a process to sleep
  354.  *
  355.  * P() on the semaphore should put us to sleep.  The process
  356.  * semaphore is cleared by default, so the first time we try
  357.  * to acquire it, we sleep.
  358.  *
  359.  * ASSUME: that no one will fiddle with the queue until after
  360.  *     we release the spin lock.
  361.  *
  362.  * NOTES: The process queue is now a priority queue for locking.
  363.  */
  364. int
  365. ProcSleep(queue, spinlock, token, prio, lock)
  366. PROC_QUEUE    *queue;
  367. SPINLOCK     spinlock;
  368. int        token;
  369. int        prio;
  370. LOCK *        lock;
  371. {
  372.   int     i;
  373.   PROC    *proc;
  374.   struct itimerval timeval, dummy;
  375.  
  376.   proc = (PROC *) MAKE_PTR(queue->links.prev);
  377.   for (i=0;i<queue->size;i++)
  378.   {
  379.     if (proc->prio < prio)
  380.       proc = (PROC *) MAKE_PTR(proc->links.prev);
  381.     else
  382.       break;
  383.   }
  384.  
  385.   MyProc->token = token;
  386.   MyProc->waitLock = lock;
  387.  
  388.   /* -------------------
  389.    * currently, we only need this for the ProcWakeup routines
  390.    * -------------------
  391.    */
  392.   TransactionIdStore((TransactionId) GetCurrentTransactionId(), &MyProc->xid);
  393.  
  394.   /* -------------------
  395.    * assume that these two operations are atomic (because
  396.    * of the spinlock).
  397.    * -------------------
  398.    */
  399.   SHMQueueInsertTL(&(proc->links),&(MyProc->links));
  400.   queue->size++;
  401.  
  402.   SpinRelease(spinlock);
  403.  
  404.   /* --------------
  405.    * Postgres does not have any deadlock detection code and for this 
  406.    * reason we must set a timer to wake up the process in the event of
  407.    * a deadlock.  For now the timer is set for 1 minute and we assume that
  408.    * any process which sleeps for this amount of time is deadlocked and will 
  409.    * receive a SIGALRM signal.  The handler should release the processes
  410.    * semaphore and abort the current transaction.
  411.    *
  412.    * Need to zero out struct to set the interval and the micro seconds fields
  413.    * to 0.
  414.    * --------------
  415.    */
  416.   bzero(&timeval, sizeof(struct itimerval));
  417.   timeval.it_value.tv_sec = 60;
  418.  
  419.   if (setitimer(ITIMER_REAL, &timeval, &dummy))
  420.     elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
  421.  
  422.   /* --------------
  423.    * if someone wakes us between SpinRelease and IpcSemaphoreLock,
  424.    * IpcSemaphoreLock will not block.  The wakeup is "saved" by
  425.    * the semaphore implementation.
  426.    * --------------
  427.    */
  428.   IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock);
  429.  
  430.   /* ---------------
  431.    * We were awoken before a timeout - now disable the timer
  432.    * ---------------
  433.    */
  434.   timeval.it_value.tv_sec = 0;
  435.  
  436.  
  437.   if (setitimer(ITIMER_REAL, &timeval, &dummy))
  438.     elog(FATAL, "ProcSleep: Unable to diable timer for process wakeup");
  439.  
  440.   /* ----------------
  441.    * We were assumed to be in a critical section when we went
  442.    * to sleep.
  443.    * ----------------
  444.    */
  445.   SpinAcquire(spinlock);
  446.  
  447.   return(MyProc->errType);
  448. }
  449.  
  450.  
  451. /*
  452.  * ProcWakeup -- wake up a process by releasing its private semaphore.
  453.  *
  454.  *   remove the process from the wait queue and set its links invalid.
  455.  *   RETURN: the next process in the wait queue.
  456.  */
  457. PROC *
  458. ProcWakeup(proc, errType)
  459. PROC    *proc;
  460. int    errType;
  461. {
  462.   PROC *retProc;
  463.   /* assume that spinlock has been acquired */
  464.  
  465.   retProc = (PROC *) MAKE_PTR(proc->links.prev);
  466.   SHMQueueDelete(&(proc->links));
  467.   SHMQueueElemInit(&(proc->links));
  468.  
  469.   proc->errType = errType;
  470.  
  471.   IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum, IpcExclusiveLock);
  472.  
  473.   return retProc;
  474. }
  475.  
  476.  
  477. /*
  478.  * ProcGetId --
  479.  */
  480. int
  481. ProcGetId()
  482. {
  483.   return( MyProc->procId );
  484. }
  485.  
  486. /*
  487.  * ProcLockWakeup -- routine for waking up processes when a lock is
  488.  *     released.
  489.  */
  490. int
  491. ProcLockWakeup( queue, ltable, lock)
  492. PROC_QUEUE    *queue;
  493. char *        ltable;
  494. char *        lock;
  495. {
  496.   PROC    *proc;
  497.   int    count;
  498.  
  499.   if (! queue->size)
  500.     return(STATUS_NOT_FOUND);
  501.  
  502.   proc = (PROC *) MAKE_PTR(queue->links.prev);
  503.   count = 0;
  504.   while ((LockResolveConflicts ((LOCKTAB *) ltable,
  505.                 (LOCK *) lock,
  506.                 proc->token,
  507.                 proc->xid,
  508.                 proc->pid) == STATUS_OK))
  509.   {
  510.     /* there was a waiting process, grant it the lock before waking it
  511.      * up.  This will prevent another process from seizing the lock
  512.      * between the time we release the lock master (spinlock) and
  513.      * the time that the awoken process begins executing again.
  514.      */
  515.     GrantLock((LOCK *) lock, proc->token);
  516.     queue->size--;
  517.     /*
  518.      * ProcWakeup removes proc from the lock waiting process queue and
  519.      * returns the next proc in chain.  If a writer just dropped
  520.      * its lock and there are several waiting readers, wake them all up.
  521.      */
  522.     proc = ProcWakeup(proc, NO_ERROR);
  523.  
  524.     count++;
  525.     if (queue->size == 0)
  526.       break;
  527.   }
  528.  
  529.   if (count)
  530.     return(STATUS_OK);
  531.   else
  532.     /* Something is still blocking us.  May have deadlocked. */
  533.     return(STATUS_NOT_FOUND);
  534. }
  535.  
  536. void
  537. ProcAddLock(elem)
  538. SHM_QUEUE    *elem;
  539. {
  540.   SHMQueueInsertTL(&MyProc->lockQueue,elem);
  541. }
  542.  
  543. /* --------------------
  544.  * We only get to this routine I sleep for a minute 
  545.  * waiting for a lock to be released by some other process.  After
  546.  * the one minute deadline we assume we have a deadlock and must abort
  547.  * this transaction.  We must also indicate that I'm no longer waiting
  548.  * on a lock so that other processes don't try to wake me up and screw 
  549.  * up my semaphore.
  550.  * --------------------
  551.  */
  552. int
  553. HandleDeadLock()
  554. {
  555.   LOCK *lock;
  556.   LOCKT lockt;
  557.  
  558.   LockLockTable();
  559.  
  560.   /* ---------------------
  561.    * Check to see if we've been awoken by anyone in the interim.
  562.    *
  563.    * If we have we can return and resume our transaction -- happy day.
  564.    * Before we are awoken the process releasing the lock grants it to
  565.    * us so we know that we don't have to wait anymore.
  566.    * 
  567.    * Damn these names are LONG! -mer
  568.    * ---------------------
  569.    */
  570.   if (IpcSemaphoreGetCount(MyProc->sem.semId, MyProc->sem.semNum) == 
  571.                                                 IpcSemaphoreDefaultStartValue)
  572.   {
  573.     UnlockLockTable();
  574.     return 1;
  575.   }
  576.  
  577.   lock = MyProc->waitLock;
  578.   lockt = (LOCKT) MyProc->token;
  579.  
  580.   /* ----------------------
  581.    * Decrement the holders fields since we can wait no longer.
  582.    * the name hodlers is misleading, it represents the sum of the number
  583.    * of active holders (those who have acquired the lock), and the number
  584.    * of waiting processes trying to acquire the lock.
  585.    * ----------------------
  586.    */
  587.   LockDecrWaitHolders(lock, lockt);
  588.  
  589.   /* ------------------------
  590.    * Get this process off the lock's wait queue
  591.    * ------------------------
  592.    */
  593.   SHMQueueDelete(&MyProc->links);
  594.   SHMQueueElemInit(&(MyProc->links));
  595.   lock->waitProcs.size--;
  596.  
  597.   UnlockLockTable();
  598.  
  599.   /* ------------------
  600.    * Unlock my semaphore, so that the count is right for next time.
  601.    * I was awoken by a signal not by someone unlocking my semaphore.
  602.    * ------------------
  603.    */
  604.   IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock);
  605.  
  606.   elog(NOTICE, "Timeout -- possible deadlock");
  607.   /* -------------
  608.    * Set MyProc->errType to STATUS_ERROR so that we abort after
  609.    * returning from this handler.
  610.    * -------------
  611.    */
  612.   MyProc->errType = STATUS_ERROR;
  613. }
  614.  
  615. void
  616. ProcReleaseSpins(proc)
  617.     PROC *proc;
  618. {
  619.     int i;
  620.  
  621.     if (!proc)
  622.     proc = MyProc;
  623.  
  624.     if (!proc)
  625.     return;
  626.     for (i=0; i < (int)MAX_SPINS; i++)
  627.     {
  628.     if (proc->sLocks[i])
  629.     {
  630.         Assert(proc->sLocks[i] == 1);
  631.         SpinRelease(i);
  632.     }
  633.     }
  634. }
  635.